package com.toplabs.unashu.exception.handler;

import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.core.map.MapUtil;
import com.toplabs.unashu.common.response.CommonResponse;
import com.toplabs.unashu.common.response.ResponseStatus;
import com.toplabs.unashu.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.connection.PoolException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.Map;

/**
 * @author toplabs
 * @since 2022/11/21
 * <p>This java file was created by toplabs in 2022/11/21.
 * The following is the description information about this file:</p>
 * <p>description: 控制器异常处理程序</p>
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Map<String, String> map =
            MapUtil.builder(NotLoginException.NOT_TOKEN, "NOT_TOKEN")
                    .put(NotLoginException.INVALID_TOKEN, "INVALID_TOKEN")
                    .put(NotLoginException.TOKEN_TIMEOUT, "TOKEN_TIMEOUT")
                    .put(NotLoginException.BE_REPLACED, "BE_REPLACED")
                    .put(NotLoginException.KICK_OUT, "KICK_OUT").build();

    @ExceptionHandler(ServiceException.class)
    public CommonResponse<String> handleServiceException(HttpServletRequest request, ServiceException e) {
        log.error("Request URL : {}, handleServiceException : {}", request.getRequestURL(), e.getMessage());
        return CommonResponse.result(e.getCode() == 0 ? HttpStatus.INTERNAL_SERVER_ERROR.value() : e.getCode(), e.getMessage());
    }

    @ExceptionHandler(NotLoginException.class)
    public CommonResponse<String> handleNotLoginException(HttpServletRequest request, NotLoginException e) {
        log.error("Request URL : {}, NotLoginException : {}", request.getRequestURL(), e.getMessage());
        if (map.containsKey(e.getType())) {
            ResponseStatus responseStatus = ResponseStatus.valueOf(map.get(e.getType()));
            return CommonResponse.result(responseStatus);
        }
        return CommonResponse.result(ResponseStatus.UNAUTHORIZED);
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public CommonResponse<String> handleNoHandlerFoundException(HttpServletRequest request, NoHandlerFoundException e) {
        log.error("Request URL : {}, handleNoHandlerFoundException : {}", request.getRequestURL(), e);
        return CommonResponse.result(HttpStatus.NOT_FOUND.value(), "路径不存在，请检查路径是否正确");
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public CommonResponse<String> handleDuplicateKeyException(HttpServletRequest request, DuplicateKeyException e) {
        log.error("Request URL : {}, handleDuplicateKeyException : {}", request.getRequestURL(), e);
        return CommonResponse.result(HttpStatus.INTERNAL_SERVER_ERROR.value(), "数据库中已存在该记录");
    }

    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public CommonResponse<String> handleSqlIntegrityConstraintViolationException(HttpServletRequest request, SQLIntegrityConstraintViolationException e) {
        log.error("Request URL : {}, handleSQLIntegrityConstraintViolationException : {}", request.getRequestURL(), e);
        return CommonResponse.result(HttpStatus.INTERNAL_SERVER_ERROR.value(), "数据库中已存在该记录");
    }

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public CommonResponse<String> handleMaxUploadSizeExceededException(HttpServletRequest request, MaxUploadSizeExceededException e) {
        log.error("Request URL : {}, handleMaxUploadSizeExceededException : {}", request.getRequestURL(), e);
        return CommonResponse.result(HttpStatus.INTERNAL_SERVER_ERROR.value(), "文件大小超出限制, 请压缩或降低文件质量! ");
    }

    @ExceptionHandler(DataIntegrityViolationException.class)
    public CommonResponse<String> handleDataIntegrityViolationException(HttpServletRequest request, DataIntegrityViolationException e) {
        log.error("Request URL : {}, handleDataIntegrityViolationException : {}", request.getRequestURL(), e);
        return CommonResponse.result(getStatus(request).value(), e.getMessage());
    }

    /**
     * Web服务器异常
     *
     * @param request http请求
     * @param e       异常类
     * @return 自定义结果
     */
    @ExceptionHandler(ServletException.class)
    public CommonResponse<String> handleServletException(HttpServletRequest request, ServletException e) {
        log.error("Request URL : {}, handleServletException : {}", request.getRequestURL(), e);
        return CommonResponse.result(getStatus(request).value(), e.getMessage());
    }

    @ExceptionHandler(PoolException.class)
    public CommonResponse<String> handlePoolException(HttpServletRequest request, PoolException e) {
        log.error("Request URL : {}, handlePoolException : {}", request.getRequestURL(), e);
        return CommonResponse.result(getStatus(request).value(), "Redis 连接异常!");
    }

    @ExceptionHandler(Exception.class)
    public CommonResponse<String> exceptionHandler(HttpServletRequest request, Exception e) throws Exception {
        log.error("Request URL : {}, Exception : {}", request.getRequestURL(), e);
        if (AnnotationUtils.findAnnotation(e.getClass(), org.springframework.web.bind.annotation.ResponseStatus.class) != null) {
            throw e;
        }
        return CommonResponse.result(getStatus(request).value(), e.getMessage());
    }

    /**
     * 获取状态码
     *
     * @param request http请求
     * @return 状态
     */
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }
}
